#!/usr/bin/perl
###############################################################################
# perl version of readboot
###############################################################################

use strict;
use warnings;

# 0: iso, 1: bin
my $fileType = 0;

my $file = '';

sub Help {
    printf("\nUsage:");
    printf("\n\tscdinfo -iso <game.ext>\n\t\tPrint info of the Sega\\MegaCD ISO image");
    printf("\n\tscdinfo -bin <game.ext>\n\t\tPrint info of the Sega\\MegaCD BIN image");
    #printf("\n\nFor any question or add, email me at Kaneda@softhome.net\n");
    printf("\n\n");
}


if ( $#ARGV == 1 ) {
    if ( $ARGV[0] eq '-iso' ) {
	$fileType = 0;
	$file = $ARGV[1];
    } elsif ( $ARGV[0] eq '-bin' ) {
	$fileType = 1;
	$file = $ARGV[1];
    } else {
	printf("Option %s unknown.\n\n", $ARGV[0]);
	&Help();
	exit 0;
    }
} else {
    &Help();
    exit 0;
}

my %companyCodeToCompanyName = (
    'ACLD' => 'Ballistic',
    'ASCI' => 'Asciiware',
    'RSI'  => 'Razorsoft',
    'SEGA' => 'SEGA',
    'TREC' => 'Treco',
    'VRGN' => 'Virgin Games',
    'WSTN' => 'Westone',
    '10'   => 'Takara',
    '11'   => 'Taito or Accolade',
    '12'   => 'Capcom',
    '13'   => 'Data East',
    '14'   => 'Namco or Tengen',
    '15'   => 'Sunsoft',
    '16'   => 'Bandai',
    '17'   => 'Dempa',
    '18'   => 'Technosoft',
    '19'   => 'Technosoft',
    '20'   => 'Asmik',
    '22'   => 'Micronet',
    '23'   => 'Vic Tokai',
    '24'   => 'Am. Sammy',
    '25'   => 'NCS',
    '28'   => 'Hot-B',
    '29'   => 'Kyugo',
    '32'   => 'WolfTeam',
    '33'   => 'Kaneko',
    '35'   => 'Toaplan',
    '36'   => 'Tecmo',
    '40'   => 'Toaplan',
    '42'   => 'UFL Company Limited',
    '43'   => 'Human',
    '45'   => 'Game Arts',
    '47'   => 'Sage\'s Creaation',
    '48'   => 'Tengen',
    '49'   => 'Renovation or Telenet or Riot',
    '50'   => 'Electronic Arts',
    '56'   => 'Razorsoft',
    '58'   => 'Mentrix',
    '60'   => 'Victor Musical Industries or JVC',
    '66'   => 'Tengen',
    '69'   => 'Arena',
    '70'   => 'Virgin',
    '73'   => 'Soft Vision',
    '74'   => 'Palsoft',
    '76'   => 'Koei',
    '79'   => 'US Gold',
    '81'   => 'Acclaim or Flying Edge',
    '83'   => 'Gametek',
    '86'   => 'Absolute',
    '93'   => 'Sony',
    '95'   => 'Konami',
    '97'   => 'Tradewest',
    '100'  => 'T*HQ Software',
    '101'  => 'Tecmagik',
    '112'  => 'Designer Software',
    '113'  => 'Psygnosis',
    '115'  => 'CORE Design',
    '119'  => 'Accolade',
    '120'  => 'CodeMasters',
    '125'  => 'Interplay',
    '130'  => 'Activision',
    '132'  => 'Shiny & Playmates',
    '144'  => 'Atlus',
    '151'  => 'Infogrames',
    '161'  => 'Fox Interactive',
    '239'  => 'Disney Interactive'
    );

my %ioTypeToName = (
    'J' => 'Joypad',
    '4' => 'MultiTap',
    '6' => 'Joypad-6',
    '0' => 'MasterSystem Joypad',
    'K' => 'Keyboard',
    'R' => 'Serial RS232C',
    'P' => 'Printer',
    'T' => 'Tablet',
    'B' => 'TrackBall',
    'V' => 'Paddle Controller',
    'F' => 'Floppy Disk Drive',
    'C' => 'CD Rom',
    'L' => 'Activator',
    'M' => 'Mouse',
    'A' => 'Analog Joystick'
    );

my $fileSize = -s $file;

open( FILE, $file ) or die "Could not read file: $file\n";

binmode FILE;

my $fileBuffer;

&CheckSectorFormats();

close FILE;


sub GetDoubleByteValue {
    my ($data) = @_;
    return ( ( ord(substr($data,0,1)) <<  8 ) |
	     ( ord(substr($data,1,1))       ) );
}

sub GetQuadByteValue {
    my ($data) = @_;
    return ( ( ord(substr($data,0,1)) << 24 ) |
	     ( ord(substr($data,1,1)) << 16 ) |
	     ( ord(substr($data,2,1)) <<  8 ) |
	     ( ord(substr($data,3,1))       ) );
}

sub SeekSectorMode1Data {
    my ($sector) = @_;
    if ( $fileType == 1 ) {
	# bin format
	seek( FILE, 2352 * $sector + 16, 0 );
    } else {
	# iso format
	seek( FILE, 2048 * $sector     , 0 );
    }
}

sub CheckSectorFormats {
    my @modes = (0,0,0);
    my $nonDataInfo = 0;
    if ( $fileType == 1 ) {
	# bin format
	my $sector = 0;
	for ( my $fileIndex = 0;
	      $fileIndex + 16 <= $fileSize;
	      $fileIndex += 2352 ) {
	    seek( FILE, $fileIndex, 0 );
	    my $fileBuffer;
	    read(FILE,$fileBuffer,16);
	    my $mode = ord(substr($fileBuffer,15,1));
	    if ( $mode > 2 ||
		 substr($fileBuffer,0,12) ne (chr(0x00).(chr(0xFF)x10).chr(0x00)) ) {
		$nonDataInfo = 1;
		last;
	    } else {
		$modes[$mode]++;
		if ( $sector == 0 ) {
		    if ( $mode == 1 ) {
			&CheckSegaBootSector();
		    } else {
			printf("First sector is not mode 1 for sega cd\n");
		    }
		}
		if ( $sector == 16 ) {
		    if ( $mode == 1 ) {
			&CheckPrimaryVolumeDescriptor();
		    } else {
			printf("First sector is not mode 1 for primary volume descriptor\n");
		    }
		}
	    }
	    $sector++;
	}
    } else {
	# iso format
	$modes[1] = int( $fileSize / 2048 );
	if ( $fileSize % 2048 ) {
	    $nonDataInfo = 1;
	}
	seek(FILE,0,0);
	&CheckSegaBootSector();
	seek(FILE,16*2048,0);
	&CheckPrimaryVolumeDescriptor();
    }
    for ( my $mode = 0; $mode <= 2; $mode++ ) {
	my $sectors = $modes[$mode];
	if ( $sectors ) {
	    print "Sectors of mode $mode: $sectors\n";
	}
    }
    if ( $nonDataInfo ) {
	print "Found non-data sector\n";
    }
}

sub CheckSegaBootSector {
    read(FILE,$fileBuffer,0x10);
    my $bootSystem = $fileBuffer;

    if ( substr($bootSystem,0,4) ne 'SEGA' ) {
	if ($fileType) {
	    printf("Not a Sega\\MegaCD dump file\n");
	} else {
	    printf("Not a Sega\\MegaCD game");
	}
	return;
    }

    read(FILE,$fileBuffer,0x0B);
    my $cdType = $fileBuffer;
    read(FILE,$fileBuffer,0x01);
    # zero
    read(FILE,$fileBuffer,0x02);
    my $volumeVersion = &GetDoubleByteValue($fileBuffer);
    read(FILE,$fileBuffer,0x02);
    my $volumeType = &GetDoubleByteValue($fileBuffer);
    read(FILE,$fileBuffer,0x0B);
    my $systemName = $fileBuffer;
    read(FILE,$fileBuffer,0x01);
    # zero
    read(FILE,$fileBuffer,0x02);
    my $systemVersion = &GetDoubleByteValue($fileBuffer);
    read(FILE,$fileBuffer,0x02);
    # zero
    read(FILE,$fileBuffer,0x04);
    my $ipAddr = &GetQuadByteValue($fileBuffer);
    read(FILE,$fileBuffer,0x04);
    my $ipLoadSize = &GetQuadByteValue($fileBuffer);
    read(FILE,$fileBuffer,0x04);
    my $ipEntry = &GetQuadByteValue($fileBuffer);
    read(FILE,$fileBuffer,0x04);
    my $ipWorkSize = &GetQuadByteValue($fileBuffer);
    read(FILE,$fileBuffer,0x04);
    my $spAddr = &GetQuadByteValue($fileBuffer);
    read(FILE,$fileBuffer,0x04);
    my $spLoadSize = &GetQuadByteValue($fileBuffer);
    read(FILE,$fileBuffer,0x04);
    my $spEntry = &GetQuadByteValue($fileBuffer);
    read(FILE,$fileBuffer,0x04);
    my $spWorkSize = &GetQuadByteValue($fileBuffer);
    read(FILE,$fileBuffer,0xB0);

    read(FILE,$fileBuffer,0x10);
    my $systemNeeded = $fileBuffer;
    read(FILE,$fileBuffer,0x10);
    my $copyright = $fileBuffer;
    read(FILE,$fileBuffer,0x30);
    my $name = $fileBuffer;
    read(FILE,$fileBuffer,0x30);
    my $nameOverseas = $fileBuffer;
    read(FILE,$fileBuffer,0x02);
    my $gameType = $fileBuffer;
    read(FILE,$fileBuffer,0x0E);
    my $productCode = $fileBuffer;
    read(FILE,$fileBuffer,0x10);
    my $io = $fileBuffer;
    read(FILE,$fileBuffer,0x2E);
    read(FILE,$fileBuffer,0x0A);
    my $modem = $fileBuffer;
    read(FILE,$fileBuffer,0x18);
    read(FILE,$fileBuffer,0x10);
    my $country = $fileBuffer;

    printf("Sega boot system: %.16s\n",$bootSystem);
    printf("Sega volume name: %.11s\n",$cdType);
    printf("Sega volume version: %.2s\n",$volumeVersion);
    printf("Sega volume type: %.2s\n",$volumeType);
    printf("Sega system name: %.11s\n",$systemName);
    printf("Sega system version: %.2s\n",$systemVersion);
    printf("Sega IP : 0x%8.8X 0x%8.8X 0x%8.8X 0x%8.8X\n", $ipAddr, $ipLoadSize, $ipEntry, $ipWorkSize);
    printf("Sega SP : 0x%8.8X 0x%8.8X 0x%8.8X 0x%8.8X\n", $spAddr, $spLoadSize, $spEntry, $spWorkSize);

    printf("Sega system needed: %.16s\n", $systemNeeded);
    printf("Sega copyright: %.16s\n", $copyright);
    printf("Sega game title: %.48s\n", $name);
    printf("Sega game title overseas: %.48s\n", $nameOverseas);
    printf("Sega game type: %.2s\n", $gameType);
    printf("Sega verbose product code: %.14s\n", $productCode);
    printf("Sega IO: %.16s\n", $io);
    printf("Sega modem: %.10s\n", $modem);
    printf("Sega country: %.16s\n", $country);

    # my $companyType = substr($copyright,3,1);

    # if ( $companyType eq 'T' ) {
    # 	my $companyCode = int(substr($copyright,5,3));
    # 	if ( $companyCode =~ m/^[0-9]{3}$/ ) {
    # 	    my $companyCode = int(substr($copyright,5,2));
    # 	}
    # 	if ( exists $companyCodeToCompanyName{$companyCode} ) {
    # 	    printf("Developed by %s\n", $companyCodeToCompanyName{$companyCode});
    # 	} else {
    # 	    printf("I don't know, let me know the ref (%d) and company\n", $companyCode);
    # 	}
    # } elsif ( $companyType eq 'S' ) {
    # 	if ( substr($country,0,1) eq 'U' ) {
    # 	    if ( substr($reference,0,1) eq 'M' ) {
    # 		printf("Developped by SEGA\n");
    # 	    } else {
    # 		printf("Developped by SEGA US (SOA)\n");
    # 	    }
    # 	} else {
    # 	    printf("Developped by SEGA\n");
    # 	}
    # }

    # my $ioType = substr($io,0,1);
    # if ( exists $ioTypeToName{$ioType} ) {
    # 	printf("Use %s\n", $ioTypeToName{$ioType});
    # } else {
    # 	printf("IO Data unknown.\nPlease let me know %c is unrecognized.", $io);
    # }

    # my $countryType = substr($country,0,1);
    # if ( $countryType eq 'J' ) {
    # 	printf("Released for Japan\n");
    # } elsif ( $countryType eq 'U' ) {
    # 	printf("Released for USA\n");
    # } elsif ( $countryType eq 'E' ) {
    # 	printf("Released for Europe\n");
    # } else {
    # 	printf("Released for Europe or Hong Kong.\nLet me know %c is unrecognized\n", $country);
    # }
}

sub CheckPrimaryVolumeDescriptor {
    read(FILE,$fileBuffer,0x01+0x05+0x01+0x01);
    if ( $fileBuffer eq (chr(0x01)."CD001".chr(0x01).chr(0x00)) ) {
	printf("Primary volume descriptor found\n");
    }
    read(FILE,$fileBuffer,0x20);
    printf("Primary volume descriptor system identifier: %s\n",$fileBuffer);
    read(FILE,$fileBuffer,0x20);
    printf("Primary volume descriptor volume identifier: %s\n",$fileBuffer);
    read(FILE,$fileBuffer,0x08);

    # set 8 bytes as both endian double word total number of sectors
    read(FILE,$fileBuffer,0x04);
    read(FILE,$fileBuffer,0x04);

    read(FILE,$fileBuffer,0x20);

    read(FILE,$fileBuffer,0x02);
    read(FILE,$fileBuffer,0x02);

    read(FILE,$fileBuffer,0x02);
    read(FILE,$fileBuffer,0x02);

    read(FILE,$fileBuffer,0x02);
    read(FILE,$fileBuffer,0x02);

    # path crap
    read(FILE,$fileBuffer,0x04);
    read(FILE,$fileBuffer,0x04);
    read(FILE,$fileBuffer,0x04);
    read(FILE,$fileBuffer,0x04);
    read(FILE,$fileBuffer,0x04);
    read(FILE,$fileBuffer,0x04);

    # root extent
    read(FILE,$fileBuffer,0x22);

    read(FILE,$fileBuffer,0x80);
    printf("Primary volume descriptor volume set identifier: %.128s\n",$fileBuffer);
    read(FILE,$fileBuffer,0x80);
    printf("Primary volume descriptor publisher identifier: %.128s\n",$fileBuffer);
    read(FILE,$fileBuffer,0x80);
    printf("Primary volume descriptor preparer identifier: %.128s\n",$fileBuffer);
    read(FILE,$fileBuffer,0x80);
    printf("Primary volume descriptor application identifier: %.128s\n",$fileBuffer);
    read(FILE,$fileBuffer,0x25);
    printf("Primary volume descriptor copyright file identifier: %.37s\n",$fileBuffer);
    read(FILE,$fileBuffer,0x25);
    printf("Primary volume descriptor abstract file identifier: %.37s\n",$fileBuffer);
    read(FILE,$fileBuffer,0x25);
    printf("Primary volume descriptor bibliographical file identifier: %.37s\n",$fileBuffer);

    # # set 17 bytes to date and time of volume creation
    # &PrintCurrentTimePVDToIso();
    # # set 17 bytes to date and time of most recent modification
    # &PrintCurrentTimePVDToIso();
    # # set 17 bytes to date and time of volume expires
    # &PrintUnspecifiedTimePVDToIso();
    # # set 17 bytes to date and time of volume is effective
    # &PrintCurrentTimePVDToIso();

    # # set 1 byte to 1
    # &PrintByteToIso(0x01);
    # # set 1 byte to 0
    # &PrintByteToIso(0x00);

    # # set 512 bytes reserved for application use
    # &PrintToIso(chr(0x20)x512);

    # # set 653 bytes to zeros
    # &PrintToIso(chr(0x00)x653);
}
